/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.isfd.util;

import com.comac.ins.common.core.exception.base.BaseException;
import com.comac.ins.common.core.utils.StringUtils;
import com.comac.ins.common.oss.core.OssClient;
import com.comac.ins.common.oss.entity.UploadResult;
import com.comac.ins.common.oss.factory.OssFactory;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

@Component
@Slf4j
public class ImportDataFileStorageUtil {


    private final ObjectMapper objectMapper = new ObjectMapper();


    /**
     * zip文件解压到临时目录
     */
    public Path unzipFileToTempDir(MultipartFile file, Path targetDir) throws IOException {
        // 构建临时文件存储位置
        Files.createDirectories(targetDir);
        // 解压 zip 到目标目录
        try (ZipInputStream zis = new ZipInputStream(file.getInputStream())) {
            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                Path entryPath = targetDir.resolve(entry.getName()).normalize();

                // Zip Slip 防护
                if (!entryPath.startsWith(targetDir)) {
                    throw new IOException("非法 zip 路径：" + entry.getName());
                }

                if (entry.isDirectory()) {
                    Files.createDirectories(entryPath);
                } else {
                    Files.createDirectories(entryPath.getParent());
                    Files.copy(zis, entryPath, StandardCopyOption.REPLACE_EXISTING);
                }
            }
        }

        validateUploadDirStructure(targetDir);
        return targetDir;
    }

    /**
     * 验证解压后的文件夹结构
     * @param uploadDir
     * @throws IOException
     */
    public void validateUploadDirStructure(Path uploadDir) throws IOException {
        // 1. 必须有data.json
        Path dataJson = uploadDir.resolve("data.json");
        if (!Files.exists(dataJson) || !Files.isRegularFile(dataJson)) {
            throw new IOException("缺少 data.json 文件");
        }
        // 2. 必须有files文件夹
        Path fileDir = uploadDir.resolve("files");
        if (!Files.exists(fileDir) || !Files.isDirectory(fileDir)) {
            throw new IOException("缺少 files/ 目录");
        }
    }


    public List<Map<String, Object>> convertJsonFileToDataList(Path uploadDir) {
        Path dataJson = uploadDir.resolve("data.json");  // 注意这里是文件，不是目录

        if (!Files.exists(dataJson) || !Files.isRegularFile(dataJson)) {
            throw new BaseException("缺少 data.json 文件");
        }

        ObjectMapper mapper = new ObjectMapper();
        try {
            return mapper.readValue(dataJson.toFile(), new TypeReference<List<Map<String, Object>>>() {});
        } catch (IOException e) {
            throw new BaseException("解析 data.json 文件失败：" + e.getMessage());
        }
    }


    public List<Map<String, Object>> convertJsonFileToDataList(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BaseException("文件不能为空！");
        }
        try {
            return objectMapper.readValue(file.getInputStream(), new TypeReference<List<Map<String, Object>>>() {});
        } catch (IOException e) {
            throw new BaseException("JSON 文件解析失败: " + e.getMessage());
        }
    }





    /**
     * 保存转换后的数据和原始文件
     * @param validatedData 校验后的数据
     * @return 存储目录路径
     */
    public String saveToDisk(Path targetDir, List<Map<String, List<Map<String, Object>>>> validatedData, MultipartFile file) {
        try {
            // 创建目录（如果不存在）
            if (Files.notExists(targetDir)) {
                Files.createDirectories(targetDir);
            }

            List<String> filePaths = new ArrayList<>();

            if (file != null && !file.isEmpty()) {
                // 提取干净的文件名
                String originalFileName = Paths.get(file.getOriginalFilename()).getFileName().toString();

                // 强制使用你指定的路径
                Path absoluteTargetDir = targetDir.toAbsolutePath();
                Files.createDirectories(absoluteTargetDir); // 只创建你指定的目录，不自动套娃

                // 拼接保存路径
                Path savePath = absoluteTargetDir.resolve(originalFileName);

                // 把上传文件保存到目标位置
                file.transferTo(savePath.toFile());
                FileUtils.setPermissions(savePath, true);

                filePaths.add(savePath.toString());
            }

            // 保存校验后的数据
            Path validatedDataPath = targetDir.resolve("validated_data.json");
            objectMapper.writeValue(validatedDataPath.toFile(), validatedData);

            // 保存元数据
            Map<String, Object> metadata = new HashMap<>();
            metadata.put("filePaths", filePaths);
            metadata.put("timestamp", LocalDateTime.now().toString());

            Path metadataPath = targetDir.resolve("metadata.json");
            objectMapper.writeValue(metadataPath.toFile(), metadata);

            log.info("数据已保存到目录：{}", targetDir);
            return targetDir.toString();
        } catch (IOException e) {
            throw new RuntimeException("保存数据到磁盘失败: " + targetDir + ", 错误: " + e.getMessage(), e);
        }
    }


    /**
     * 从磁盘读取数据
     * @param directoryPath 存储目录路径
     * @return 读取的数据（转换后的数据 + 文件路径）
     */
    public Map<String, Object> getFromDisk(String directoryPath) {
        try {
            // 读取转换后的数据
            File validatedDataFile = new File(directoryPath, "validated_data.json");
            List<Map<String, List<Map<String, Object>>>> validatedData = objectMapper.readValue(validatedDataFile, List.class);

            // 读取元数据
            File metadataFile = new File(directoryPath, "metadata.json");
            Map<String, Object> metadata = objectMapper.readValue(metadataFile, Map.class);

            // 组合返回
            Map<String, Object> result = new HashMap<>();
            result.put("validatedData", validatedData);
            result.put("filePaths", metadata.get("filePaths"));
            return result;
        } catch (IOException e) {
            throw new RuntimeException("读取文件失败: " + e.getMessage(), e);
        }
    }

    /**
     * 删除存储目录
     */
    public void deleteDirectory(Path dir) {
        if (dir == null || !Files.exists(dir)) {
            return;
        }
        try (Stream<Path> walk = Files.walk(dir)) {
            walk.sorted(Comparator.reverseOrder())  // 先删除子文件/子目录
                .forEach(path -> {
                    try {
                        Files.delete(path);
                    } catch (IOException e) {
                        throw new RuntimeException("删除失败：" + path.toString(), e);
                    }
                });
        } catch (IOException e) {
            throw new RuntimeException("删除目录失败：" + dir.toString(), e);
        }
    }

    /**
     * 验证文件是否存在
     * @param filePath
     * @return
     */
    public boolean isFileExists(Path filePath) {
        return filePath != null && Files.exists(filePath) && Files.isRegularFile(filePath);
    }

    /**
     * 保存长传的文件到指定文件夹
     */
    public Path saveMultipartFile(Path targetDir, MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("文件为空");
        }

        try {
            // 创建目标目录（如果不存在）
            if (Files.notExists(targetDir)) {
                Files.createDirectories(targetDir);
            }

            // 获取安全的文件名，防止路径穿越
            String originalFileName = Paths.get(file.getOriginalFilename()).getFileName().toString();

            Path savePath = targetDir.resolve(originalFileName);

            // 保存文件
            try (InputStream in = file.getInputStream()) {
                Files.copy(in, savePath, StandardCopyOption.REPLACE_EXISTING);
            }

            return savePath;
        } catch (IOException e) {
            log.error("保存文件失败: " + targetDir, e);
            throw new RuntimeException("保存文件失败: " + targetDir, e);
        }
    }




    /**
     * 上传文件到minio
     *
     */
    public UploadResult uploadFileToOss(Path filePath, String basePrefix) {
        String fullPath = filePath.toAbsolutePath().toString().replace("\\", "/");

        basePrefix = basePrefix.trim().replace("\\", "/");
        // 去掉 basePrefix 前缀的 /
        if (basePrefix.startsWith("/")) {
            basePrefix = basePrefix.substring(1);
        }


        int index = fullPath.indexOf(basePrefix);
        if (index < 0) {
            throw new IllegalArgumentException("路径中不包含预期前缀：" + basePrefix);
        }

        // 截取包含前缀及后续路径部分作为key
        String key = fullPath.substring(index);

        // 去掉可能的开头 /
        if (key.startsWith("/")) {
            key = key.substring(1);
        }

        OssClient storage = OssFactory.instance();
        return storage.upload(filePath, key, null);
    }

    /**
     * 上传文件夹到minio
     * @param directory
     * @return
     */
    public List<UploadResult> uploadDirectoryToOss(Path directory, String basePrefix) {
        List<UploadResult> results = new ArrayList<>();
        if (!Files.isDirectory(directory)) {
            throw new IllegalArgumentException("提供的路径不是文件夹: " + directory);
        }

        try (Stream<Path> fileStream = Files.walk(directory)) {
            fileStream
                .filter(Files::isRegularFile)
                .forEach(file -> {
                    try {
                        UploadResult result = uploadFileToOss(file, basePrefix);
                        if (result != null && StringUtils.isNotBlank(result.getUrl())) {
                            results.add(result);
                        } else {
                            System.err.println("上传失败: " + file);
                        }
                    } catch (Exception e) {
                        System.err.println("上传文件出错: " + file + ", 错误: " + e.getMessage());
                    }
                });
        } catch (IOException e) {
            throw new RuntimeException("读取文件夹失败: " + directory, e);
        }

        return results;
    }





    /**
     * 获取云存储服务的URL
     *
     */
    public String getURL(){
        OssClient storage = OssFactory.instance();
        return storage.getUrl();
    }

    /**
     * 移除路径中的基础URL部分，得到相对路径
     */
    public String removeBaseUrl(String path, String basePrefix) {
        if (StringUtils.isBlank(path)) {
            return "";
        }
        // 替换反斜杠为正斜杠
        String normalizedPath = path.replace("\\", "/");
        // 查找 data/collect 的起始位置
        int startIndex = normalizedPath.indexOf(basePrefix);
        if (startIndex == -1) {
            return normalizedPath; // 如果没找到关键字，就返回原路径
        }

        return normalizedPath.substring(startIndex);
    }


    /**
     * 文件是否存在
     */
    public String getObjectUrlIfExist(Path filePath, String basePrefix) {
        String fullPath = filePath.toAbsolutePath().toString().replace("\\", "/");

        basePrefix = basePrefix.trim().replace("\\", "/");
        // 去掉 basePrefix 前缀的 /
        if (basePrefix.startsWith("/")) {
            basePrefix = basePrefix.substring(1);
        }


        int index = fullPath.indexOf(basePrefix);
        if (index < 0) {
            throw new IllegalArgumentException("路径中不包含预期前缀：" + basePrefix);
        }

        // 截取包含前缀及后续路径部分作为key
        String key = fullPath.substring(index);

        // 去掉可能的开头 /
        if (key.startsWith("/")) {
            key = key.substring(1);
        }

        OssClient storage = OssFactory.instance();
        try {
            return storage.getObjectUrlIfExist(key);
        } catch (Exception e) {
            return null;
        }

    }


    public void deleteLocalFile(Path filePath) {
        try {
            // 判断文件是否存在且是普通文件（非目录）
            if (filePath != null && Files.exists(filePath) && Files.isRegularFile(filePath)) {
                Files.delete(filePath);
                log.info("成功删除本地文件：{}", filePath);
            } else {
                log.warn("删除失败：路径不存在或不是普通文件：{}", filePath);
            }
        } catch (IOException e) {
            log.error("删除本地文件失败：{}，原因：{}", filePath, e.getMessage(), e);
            throw new RuntimeException("删除本地文件失败：" + filePath, e);
        }
    }


}
